#include "AtlasDefines.h"
#include "AtlasUUID.h"
#include "DDL.h"
#include "DDLReflect.h"

#include <json/json.h>

namespace DDLReflect
{
	static bool call_jsonread(DDL::MemoryReader& buf, _U8 type, const STRUCT_INFO* def, Json::Value& value);
	static bool call_jsonread(DDL::MemoryReader& buf, const FIELD_INFO* def, _U16 count, Json::Value& value);
	static bool call_jsonwrite(DDL::MemoryWriter& buf, _U8 type, const STRUCT_INFO* def, const Json::Value& value);
	static bool call_jsonwrite(DDL::MemoryWriter& buf, const FIELD_INFO* def, _U16 count, const Json::Value& value);

	static bool struct_jsonread(const _U8* buf, const STRUCT_INFO* def, Json::Value& value);
	static bool struct_jsonread(const _U8* buf, const FIELD_INFO* def, _U16 count, Json::Value& root);
	static bool struct_jsonread(const _U8* buf, _U8 type, _U16 slen, Json::Value& value);
	static bool struct_jsonwrite(const Json::Value& value, const STRUCT_INFO* def, _U8* data);
	static bool struct_jsonwrite(const Json::Value& value, const FIELD_INFO* def, _U16 count, _U8* data);
	static bool struct_jsonwrite(const Json::Value& value, _U8 type, _U16 slen, _U8* data);

	bool Call2Json(const FUNCTION_INFO* def, _U32 len, const _U8* data, std::string& json)
	{
		Json::Value root(Json::objectValue);
		DDL::MemoryReader buf(data, len);
		if(!call_jsonread(buf, def->finfos, def->fcount, root)) return false;
		Json::FastWriter writer;
		json = writer.write(root);
		return true;
	}

	bool Json2Call(const FUNCTION_INFO* def, const std::string& json, _U32& len, _U8* data)
	{
		Json::Value root;
		Json::Reader reader;
		if(!reader.parse(json, root)) return false;
		DDL::MemoryWriter buf(data, len);
		if(!call_jsonwrite(buf, def->finfos, def->fcount, root)) return false;
		len = buf.GetSize();
		return true;
	}

	bool Struct2Json(const STRUCT_INFO* def, const _U8* data, Json::Value& Value)
	{
		return struct_jsonread(data, def, Value);
	}

	bool Json2Struct(const STRUCT_INFO* def, const Json::Value& Value, _U8* data)
	{
		return struct_jsonwrite(Value, def, data);
	}

	bool Struct2Json(const STRUCT_INFO* def, const _U8* data, std::string& json)
	{
		Json::Value root(Json::objectValue);
		if(!struct_jsonread(data, def, root)) return false;
		Json::FastWriter writer;
		json = writer.write(root);
		return true;
	}

	bool Json2Struct(const STRUCT_INFO* def, const std::string& json, _U8* data)
	{
		Json::Value root;
		Json::Reader reader;
		if(!reader.parse(json, root)) return false;
		if(!struct_jsonwrite(root, def, data)) return false;
		return true;
	}

	bool call_jsonread(DDL::MemoryReader& buf, _U8 type, const STRUCT_INFO* def, Json::Value& value)
	{
		if(type&TYPE_ARRAY)
		{
			_U16 count;
			if(!buf.Read(count)) return false;
			value = Json::Value(Json::arrayValue);
			for(_U16 i=0; i<count; i++)
			{
				Json::Value svalue(Json::nullValue);
				if(!call_jsonread(buf, type&TYPE_MASK, def, svalue)) return false;
				value.append(svalue);
			}
		}
		else
		{
			switch(type&TYPE_MASK)
			{
			case TYPE_U8:
				{
					_U8 v;
					if(!buf.Read(v)) return false;
					value = (Json::UInt)v;
					break;
				}
			case TYPE_U16:
				{
					_U16 v;
					if(!buf.Read(v)) return false;
					value = (Json::UInt)v;
					break;
				}
			case TYPE_U32:
				{
					_U32 v;
					if(!buf.Read(v)) return false;
					value = (Json::UInt)v;
					break;
				}
			case TYPE_U64:
				{
					_U64 v;
					if(!buf.Read(v)) return false;
					value = (Json::UInt)v;
					break;
				}
			case TYPE_S8:
				{
					_S8 v;
					if(!buf.Read(v)) return false;
					value = (Json::Int)v;
					break;
				}
			case TYPE_S16:
				{
					_S16 v;
					if(!buf.Read(v)) return false;
					value = (Json::Int)v;
					break;
				}
			case TYPE_S32:
				{
					_S32 v;
					if(!buf.Read(v)) return false;
					value = (Json::Int)v;
					break;
				}
			case TYPE_S64:
				{
					_S64 v;
					if(!buf.Read(v)) return false;
					value = (Json::Int)v;
					break;
				}
			case TYPE_F32:
				{
					_F32 v;
					if(!buf.Read(v)) return false;
					value = (double)v;
					break;
				}
			case TYPE_F64:
				{
					_F64 v;
					if(!buf.Read(v)) return false;
					value = (double)v;
					break;
				}
			case TYPE_STRING:
				{
					_U16 len;
					if(!buf.Read(len)) return false;
					const char* str = (const char*)buf.ReadBuffer(len);
					if(!str) return false;
					value = Json::Value(str, str+len-1);
					break;
				}
			case TYPE_UUID:
			case TYPE_UUID_REF:
				{
					A_UUID uuid;
					if(!buf.Read(uuid)) return false;
					char str[100];
					AUuidToString(uuid, str);
					value = Json::Value(str);
					break;
				}
				break;
			case TYPE_STRUCT:
				{
					value = Json::Value(Json::objectValue);
					int count = 0;
					FIELD_INFO* fi_p[100];
					_U16 fi_c[100];
					const STRUCT_INFO* pStructDef = def;
					while(pStructDef)
					{
						fi_p[count] = pStructDef->finfos;
						fi_c[count] = pStructDef->fcount;
						count++;
						pStructDef = pStructDef->parent;
					}
					for(int i=count-1; i>=0; i--)
					{
						if(!call_jsonread(buf, fi_p[i], fi_c[i], value)) return false;
					}
					break;
				}
			default:
				return false;
			}
		}
		return true;
	}

	bool call_jsonread(DDL::MemoryReader& buf, const FIELD_INFO* def, _U16 count, Json::Value& root)
	{
		for(_U16 i=0; i<count; i++)
		{
			Json::Value svalue(Json::nullValue);
			if(!call_jsonread(buf, def[i].type, def[i].sinfo, svalue)) return false;
			root[def[i].name] = svalue;
		}
		return true;
	}

	bool call_jsonwrite(DDL::MemoryWriter& buf, _U8 type, const STRUCT_INFO* def, const Json::Value& value)
	{
		if(type&TYPE_ARRAY)
		{
			if(!value.isArray()) return false;
			_U16 count = value.size();
			if(!buf.Write(count)) return false;
			for(_U16 i=0; i<count; i++)
			{
				if(!call_jsonwrite(buf, type&TYPE_MASK, def, value[i])) return false;
			}
		}
		else
		{
			switch(type&TYPE_MASK)
			{
			case TYPE_U8:
				{
					if(!value.isUInt() && !value.isInt()) return false;
					_U8 v = (_U8)value.asUInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_U16:
				{
					if(!value.isUInt() && !value.isInt()) return false;
					_U16 v = (_U16)value.asUInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_U32:
				{
					if(!value.isUInt() && !value.isInt()) return false;
					_U32 v = (_U32)value.asUInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_U64:
				{
					if(!value.isUInt() && !value.isInt()) return false;
					_U64 v = (_U64)value.asUInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_S8:
				{
					if(!value.isInt()) return false;
					_S8 v = (_S8)value.asInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_S16:
				{
					if(!value.isInt()) return false;
					_S16 v = (_S16)value.asInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_S32:
				{
					if(!value.isInt()) return false;
					_S32 v = (_S32)value.asInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_S64:
				{
					if(!value.isInt()) return false;
					_S64 v = (_S64)value.asInt();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_F32:
				{
					if(!value.isDouble()) return false;
					_F32 v = (_F32)value.asDouble();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_F64:
				{
					if(!value.isDouble()) return false;
					_F64 v = (_F64)value.asDouble();
					if(!buf.Write(v)) return false;
					break;
				}
			case TYPE_STRING:
				{
					if(!value.isString()) return false;
					const char* str = value.asCString();
					_U16 len = (_U16)strlen(str);
					if(!buf.Write(len)) return false;
					if(!buf.WriteData(str, len)) return false;
					break;
				}
			case TYPE_UUID:
			case TYPE_UUID_REF:
				{
					if(!value.isString()) return false;
					A_UUID uuid;
					const char* str = value.asCString();
					if(!AUuidFromString(str, uuid)) return false;
					if(!buf.Write(uuid)) return false;
					break;
				}
				break;
			case TYPE_STRUCT:
				{
					if(!value.isObject()) return false;
					if(!call_jsonwrite(buf, def->finfos, def->fcount, value)) return false;
					break;
				}
			default:
				return false;
			}
		}
		return true;
	}

	bool call_jsonwrite(DDL::MemoryWriter& buf, const FIELD_INFO* def, _U16 count, const Json::Value& root)
	{
		if(!root.isObject()) return false;
		for(_U16 i=0; i<count; i++)
		{
			if(!root.isMember(def[i].name)) return false;
			if(!call_jsonwrite(buf, def[i].type, def[i].sinfo, root[def[i].name])) return false;
		}
		return true;
	}

	bool struct_jsonread(const _U8* buf, const STRUCT_INFO* def, Json::Value& root)
	{
		if(def->parent)
		{
			if(!struct_jsonread(buf, def->parent, root)) return false;
		}
		return struct_jsonread(buf, def->finfos, def->fcount, root);
	}

	bool struct_jsonread(const _U8* buf, const FIELD_INFO* def, _U16 count, Json::Value& root)
	{
		for(_U16 i=0; i<count; i++)
		{
			Json::ValueType type = Json::nullValue;
			switch(def[i].type&TYPE_MASK)
			{
			case TYPE_U8:
			case TYPE_U16:
			case TYPE_U32:
			case TYPE_U64:
				type = Json::uintValue;
				break;
			case TYPE_S8:
			case TYPE_S16:
			case TYPE_S32:
			case TYPE_S64:
				type = Json::intValue;
				break;
			case TYPE_F32:
			case TYPE_F64:
				type = Json::realValue;
				break;
			case TYPE_STRING:
				type = Json::stringValue;
				break;
			case TYPE_UUID:
			case TYPE_UUID_REF:
				type = Json::stringValue;
				break;
			case TYPE_STRUCT:
				type = Json::objectValue;
				break;
			default:
				return false;
			}

			if(def[i].type&TYPE_ARRAY)
			{
				Json::Value avalue(Json::arrayValue);
				_U32 len = *(_U32*)((const _U8*)buf + def[i].offset);
				if(len>def[i].alen) return false;

				for(_U32 a=0; a<len; a++)
				{
					Json::Value svalue(type);
					if((def[i].type&TYPE_MASK)==TYPE_STRUCT)
					{
						if(!struct_jsonread(buf+def[i].offset+def[i].prefix+def[i].elen*a, def[i].sinfo, svalue))
						{
							return false;
						}
					}
					else
					{
						if(!struct_jsonread(buf+def[i].offset+def[i].prefix+def[i].elen*a, def[i].type&TYPE_MASK, def[i].alen, svalue))
						{
							return false;
						}
					}
					avalue.append(svalue);
				}
				root[def[i].name] = avalue;
			}
			else
			{
				Json::Value svalue(type);
				if((def[i].type&TYPE_MASK)==TYPE_STRUCT)
				{
					if(!struct_jsonread(buf+def[i].offset, def[i].sinfo, svalue)) return false;
				}
				else 
				{
					if(!struct_jsonread(buf+def[i].offset, def[i].type, def[i].slen, svalue)) return false;
				}
				root[def[i].name] = svalue;
			}
		}
		return true;
	}

	bool struct_jsonread(const _U8* buf, _U8 type, _U16 slen, Json::Value& value)
	{
		switch(type)
		{
		case TYPE_U8:
			value = (Json::UInt)(*((const _U8*)buf));
			break;
		case TYPE_U16:
			value = (Json::UInt)(*((const _U16*)buf));
			break;
		case TYPE_U32:
			value = (Json::UInt)(*((const _U32*)buf));
			break;
		case TYPE_U64:
			value = (Json::UInt)(*((const _U64*)buf));
			break;
		case TYPE_S8:
			value = (Json::Int)(*((const _S8*)buf));
			break;
		case TYPE_S16:
			value = (Json::Int)(*((const _S16*)buf));
			break;
		case TYPE_S32:
			value = (Json::Int)(*((const _S32*)buf));
			break;
		case TYPE_S64:
			value = (Json::Int)(*((const _S64*)buf));
			break;
		case TYPE_F32:
			value = (double)(*((const _F32*)buf));
			break;
		case TYPE_F64:
			value = (double)(*((const _F64*)buf));
			break;
		case TYPE_STRING:
			if(strlen((const char*)buf)>slen) return false;
			value = (const char*)buf;
			break;
		case TYPE_UUID:
		case TYPE_UUID_REF:
			{
				char str[100];
				AUuidToString(*((const A_UUID*)buf), str);
				value = str;
				break;
			}
		default:
			return false;
		}
		return true;
	}

	bool struct_jsonwrite(const Json::Value& value, const STRUCT_INFO* def, _U8* data)
	{
		if(!value.isObject()) return false;
		if(def->parent)
		{
			if(!struct_jsonwrite(value, def->parent, data)) return false;
		}
		return struct_jsonwrite(value, def->finfos, def->fcount, data);
	}

	bool struct_jsonwrite(const Json::Value& value, const FIELD_INFO* def, _U16 count, _U8* data)
	{
		for(_U16 i=0; i<count; i++)
		{
			if(!value.isMember(def[i].name)) return false;
			const Json::Value& svalue = value[def[i].name];

			if(def[i].type&TYPE_ARRAY)
			{
				if(!svalue.isArray()) return false;

				Json::UInt a;

				//write length of array
				if(!struct_jsonwrite(svalue.size(), TYPE_U32, 0, data+def[i].offset)) return false;

				for(a=0; a<svalue.size(); a++)
				{
					if((def[i].type&TYPE_MASK)==TYPE_STRUCT)
					{
						if(!struct_jsonwrite(svalue[a], def[i].sinfo, data+def[i].offset+def[i].prefix+def[i].elen*a)) return false;
					}
					else
					{
						if(!struct_jsonwrite(svalue[a], def[i].type&TYPE_MASK, def[i].alen, data+def[i].offset+def[i].prefix+def[i].elen*a)) return false;
					}
				}
			}
			else
			{
				if(def[i].type==TYPE_STRUCT)
				{
					if(!struct_jsonwrite(svalue, def[i].sinfo, data+def[i].offset)) return false;
				}
				else 
				{
					if(!struct_jsonwrite(svalue, def[i].type, def[i].slen, data+def[i].offset)) return false;
				}
			}
		}
		return true;
	}

	bool struct_jsonwrite(const Json::Value& value, _U8 type, _U16 slen, _U8* data)
	{
		switch(type)
		{
		case TYPE_U8:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_U8*)data) = (_U8)value.asUInt();
			break;
		case TYPE_U16:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_U16*)data) = (_U16)value.asUInt();
			break;
		case TYPE_U32:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_U32*)data) = (_U32)value.asUInt();
			break;
		case TYPE_U64:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_U64*)data) = (_U64)value.asUInt();
			break;
		case TYPE_S8:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_S8*)data) = (_S8)value.asInt();
			break;
		case TYPE_S16:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_S16*)data) = (_S16)value.asInt();
			break;
		case TYPE_S32:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_S32*)data) = (_S32)value.asInt();
			break;
		case TYPE_S64:
			if(!value.isUInt() && !value.isInt()) return false;
			*((_S64*)data) = (_S64)value.asInt();
			break;
		case TYPE_F32:
			if(!value.isDouble()) return false;
			*((_F32*)data) = (_F32)value.asDouble();
			break;
		case TYPE_F64:
			if(!value.isDouble()) return false;
			*((_F64*)data) = (_F64)value.asDouble();
			break;
		case TYPE_STRING:
			{
				std::string v = value.asString();
				if(v.size()>slen) return false;
				memcpy(data, v.c_str(), v.size()+1);
			}
			break;
		case TYPE_UUID:
		case TYPE_UUID_REF:
			{
				std::string v = value.asString();
				if(!AUuidFromString(v.c_str(), *((A_UUID*)data))) return false;
			}
			break;
		default:
			return false;
		}
		return true;
	}

	bool IsParent(const STRUCT_INFO* child, const STRUCT_INFO* parent)
	{
		child = child->parent;
		while(child)
		{
			if(child==parent) return true;
			child = child->parent;
		}
		return false;
	}

	bool IsChild(const STRUCT_INFO* parent, const STRUCT_INFO* child)
	{
		return IsParent(child, parent);
	}

	void GetStructParamTypeString(const STRUCT_INFO* info, _U16 index, char* str)
	{
		if(index>=info->fcount)
		{
			strcpy(str, "invaid");
			return;
		}

		char type[1000];
		switch(info->finfos[index].type&DDLReflect::TYPE_MASK)
		{
		case DDLReflect::TYPE_U8:		sprintf(type, "%s", "_U8"); break;
		case DDLReflect::TYPE_U16:		sprintf(type, "%s", "_U16"); break;
		case DDLReflect::TYPE_U32:		sprintf(type, "%s", "_U32"); break;
		case DDLReflect::TYPE_U64:		sprintf(type, "%s", "_U64"); break;
		case DDLReflect::TYPE_S8:		sprintf(type, "%s", "_S8"); break;
		case DDLReflect::TYPE_S16:		sprintf(type, "%s", "_S16"); break;
		case DDLReflect::TYPE_S32:		sprintf(type, "%s", "_S32"); break;
		case DDLReflect::TYPE_S64:		sprintf(type, "%s", "_S64"); break;
		case DDLReflect::TYPE_F32:		sprintf(type, "%s", "_F32"); break;
		case DDLReflect::TYPE_F64:		sprintf(type, "%s", "_F64"); break;
		case DDLReflect::TYPE_STRING:	sprintf(type, "string<%d>", info->finfos[index].slen); break;
		case DDLReflect::TYPE_UUID:		sprintf(type, "%s", "A_UUID"); break;
		case DDLReflect::TYPE_UUID_REF:		sprintf(type, "%s", "A_UUID"); break;
		case DDLReflect::TYPE_STRUCT:	sprintf(type, "%s", info->finfos[index].sinfo->name); break;
		default:
			strcpy(type, "unknown");
		}
		if(info->finfos[index].type&DDLReflect::TYPE_ARRAY)
		{
			sprintf(str, "array<%s, %d>", type, info->finfos[index].alen);
		}
		else
		{
			strcpy(str, type);
		}
	}

	void* CreateObject(const STRUCT_INFO* info)
	{
		void* data = malloc(info->size);
		memset(data, 0, (size_t)info->size);
		return data;
	}

	void DestoryObject(void* data)
	{
		free(data);
	}

}
